// vending-machine
// 2 yuan for a bottle of drink
// only 2 coins supported: 5 jiao and 1 yuan
// finish the function of selling and changing

module  vending_machine_p3  (
    input           clk ,       // 时钟输入
    input           rstn ,      // 复位按钮
    input [1:0]     coin ,      // 硬币投入集 01 表示 0.5 元， 10 表示 1 元

    output [1:0]    change ,    // 找零
    output          sell        // 产品输出
    );

    // 售卖机状态
    parameter            IDLE   = 3'd0 ;            //     
    parameter            GET05  = 3'd1 ;            // 投入 0.5元
    parameter            GET10  = 3'd2 ;            // 投入 1  元
    parameter            GET15  = 3'd3 ;            // 投入 1.5元

    // 状态寄存器，分别存储当前状态和下一个状态
    reg [2:0]            st_next ;
    reg [2:0]            st_cur ;

    // 每一个时钟周期进行状态转换（根据st_next 的值转换）
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            st_cur      <= 'b0 ;
        end
        else begin
            st_cur      <= st_next ;
        end
    end

    //(2) state switch, using block assignment for combination-logic
    //all case items need to be displayed completely    
    always @(*) begin 
        //st_next = st_cur ;//如果条件选项考虑不全，可以赋初值消除latch
        case(st_cur)
            IDLE:
                case (coin)

                    // 如果投入 0.5 元，切换到GET05
                    2'b01:     st_next = GET05 ;

                    // 如果投入 1元，切换到 GET10
                    2'b10:     st_next = GET10 ;

                    // 其他情况
                    default:   st_next = IDLE ;
                endcase

            // 在投入了 0.5 元的基础上
            GET05:
                case (coin)
                    // 再投入 0.5 元，切换到 GET10
                    2'b01:     st_next = GET10 ;

                    // 再投入 1 元，切换到 GET15
                    2'b10:     st_next = GET15 ;

                    // 其他情况不变
                    default:   st_next = GET05 ;
                endcase


            // 在投入了 1元的基础上
            GET10:
                case (coin)

                    // 再投入了 0.5 元，状态切换到 GET15
                    2'b01:     st_next = GET15 ;

                    // 再投入了 1 元，状态切换到 空闲
                    2'b10:     st_next = IDLE ;

                    // 其他情况不变
                    default:   st_next = GET10 ;
                endcase

            // 在投入了 1.5 元的基础上
            GET15:
                
                case (coin)
                    // 再投入 1元或者0.5元 都会切换回空闲状态
                    2'b01,2'b10:
                               st_next = IDLE ;

                    // 其他情况不变
                    default:   st_next = GET15 ;
                endcase
            default:    st_next = IDLE ;
        endcase
    end

    //(3) 计算找零 以及  输出产品
    reg  [1:0]   change_r ;
    reg          sell_r ;
    always @(posedge clk or negedge rstn) begin

        // 如果复位按钮按下了，初始化数据
        if (!rstn) begin
            change_r       <= 2'b0 ;
            sell_r         <= 1'b0 ;
        end

        // 如果 当前状态是 GET15 而且 投币了 0.5 元
        // 如果 当前状态是 GET10 而且 投币了 1 元
        else if ((st_cur == GET15 && coin ==2'h1)
               || (st_cur == GET10 && coin ==2'd2)) begin
            
            // 找零为 0，输出产品
            change_r       <= 2'b0 ;
            sell_r         <= 1'b1 ;
        end

        // 如果 当前状态是 GET15 而且 投币了 1 元
        else if (st_cur == GET15 && coin == 2'h2) begin

            // 找零为 0.5 输出产品
            change_r       <= 2'b1 ;
            sell_r         <= 1'b1 ;
        end

        // 其他情况不输出产品、不找零
        else begin
            change_r       <= 2'b0 ;
            sell_r         <= 1'b0 ;
        end
    end
    assign       sell    = sell_r ;
    assign       change  = change_r ;

endmodule